home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AOL File Library: 2,801 to 2,900
/
aol-file-protocol-4400-2801-to-2900.zip
/
AOLDLs
/
C++ Files Library
/
SK (Sockects) 1.4.1 r2
/
SK v1.4.1 r2.sit
/
SK 1.4.1 r2
/
SK
/
SK Sources
/
SK_TCP.cc
< prev
next >
Wrap
Text File
|
1994-06-13
|
33KB
|
1,291 lines
/***************************************************************************
*
*
* Copyright ⌐ 1992-1994 Matthias Neeracher and the Decision Systems Group
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to redistribute it freely, subject to the
* following restrictions:
*
* 1) The authors and the Decision Systems Group are not responsible for
* the direct or indirect consequences of use of this software, no matter
* how awful, even if they arise from defects in the software itself.
* This restriction applies to the use of this and any derived source code
* and also to the use of all binary produced from this and any derived
* source code.
* 2) The origin and copyrights of this software must not be misrepresented,
* either by explicit claim or by omission or alteration of copyright or
* authorship header information in this file or in any derived file.
* 3) Altered or derived versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* We encourage users of this software to provide feedback, bug fixes,
* and enhancements to the authors for incorporation into future releases.
*
* ==========================================================================
*
* FILE: SK_INET.cc
*
* AUTHOR: Matthias Neeracher and Stephan Deibel
*
* CREATION DATE: 12Aug92
*
* VERSION: 13Jun94
*
* DESCRIPTION:
*
* TCP stream sockets implementation
*
* NOTES:
*
* 0) This code was derived from Matthias Neerarcher's GUSI 1.1.0 and
* the socket library written by Charlie Reiman (creiman@ncsa.uiuc.edu)
* and Tom Milligan (milligan@madhaus.utcs.utoronto.ca); Copyrights
* are subject to this origin.
*
* MODIFICATIONS:
* --------------------------------------------------------------------------
* Date Name Description of modification
* --------------------------------------------------------------------------
*
* 08Sep92 MN A big part should work now
* 08Jan93 MN tcp_notify was setting the wrong state to unconnected
* 17Jan93 MN Be more careful about user interrupts.
* 21Jan93 MN Simplify and correct code
* 31Jan93 MN Support for inetd
* 13Jul93 SD Port to THINK C++ 6.0
* 21Jul93 SD Subsetted to only TCP/UDP, applied DSG naming
* conventions and commenting guidelines, and
* changed name to "SK" for clear distinction from
* the GUSI code from which this was derived
* 13Jun94 SD Updated based on relevant changes to GUSI (through 1.4.1)
*
*/
#include "SK_INET_P.hh"
/* All completion procedures must use "C" calling and not "C++" -- needed */
/* primarily because of bugs with the THINK C++ 6.0 compiler */
extern "C" {
/**************************************************************************/
/**************************************************************************/
/* Completion procedures */
/*************************************************************************** *
* FUNCTION:
*
* tcp_notify
*
* DESCRIPTION:
*
* TCP notify call back
*
* PARAMETERS:
*
* StreamPtr -- ???
* u_short -- ???
* Ptr -- ???
* u_short -- ???
* struct ICMPReport -- ???
*
*/
pascal void tcp_notify(
StreamPtr,
u_short eventCode,
Ptr userDataPtr,
u_short,
struct ICMPReport *)
{
CSK_TCPSocket * sock = *(CSK_TCPSocket **) userDataPtr;
switch (eventCode) {
case TCPClosing:
sock->fSocketState = SKk_STATE_CLOSING;
break;
case TCPTerminate:
sock->fSocketState = SKk_STATE_UNCONNECTED;
break;
}
}
/*************************************************************************** *
* FUNCTION:
*
* tcp_connect_done
*
* DESCRIPTION:
*
* TCP connect-done call back
*
* PARAMETERS:
*
* CSK_AnnotatedPB * -- ???
*
*/
void tcp_connect_done(CSK_AnnotatedPB *pb)
{
CSK_TCPSocket * sock = (CSK_TCPSocket *) pb->Owner();
TCPiopb * tcp = pb->TCP();
if (!tcp->ioResult) {
sock->fMySocketAddress.sin_addr.s_addr = tcp->csParam.open.localHost;
sock->fMySocketAddress.sin_port = tcp->csParam.open.localPort;
sock->fPeerSocketAddress.sin_addr.s_addr = tcp->csParam.open.remoteHost;
sock->fPeerSocketAddress.sin_port = tcp->csParam.open.remotePort;
sock->fSocketState = SKk_STATE_CONNECTED;
sock->fLastAsyncErr = noErr;
}
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(tcp->ioResult != inProgress);
}
/*************************************************************************** *
* FUNCTION:
*
* tcp_listen_done
*
* DESCRIPTION:
*
* TCP listen-done call back
*
* PARAMETERS:
*
* CSK_AnnotatedPB * -- ???
*
*/
void tcp_listen_done(CSK_AnnotatedPB *pb)
{
CSK_TCPSocket * sock = (CSK_TCPSocket *) pb->Owner();
TCPiopb * tcp = pb->TCP();
switch(tcp->ioResult) {
case noErr:
sock->fPeerSocketAddress.sin_addr.s_addr = tcp->csParam.open.remoteHost;
sock->fPeerSocketAddress.sin_port = tcp->csParam.open.remotePort;
sock->fSocketState = SKk_STATE_LIS_CON;
sock->fLastAsyncErr = 0;
break;
case openFailed:
case invalidStreamPtr:
case connectionExists:
case duplicateSocket:
case commandTimeout:
default:
sock->fSocketState = SKk_STATE_UNCONNECTED;
sock->fLastAsyncErr = tcp->ioResult;
break;
}
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(tcp->ioResult != inProgress);
}
/*************************************************************************** *
* FUNCTION:
*
* tcp_recv_done
*
* DESCRIPTION:
*
* TCP recv-done call back
*
* PARAMETERS:
*
* CSK_AnnotatedPB * -- ???
*
*/
void tcp_recv_done(CSK_AnnotatedPB *pb)
{
CSK_TCPSocket * sock = (CSK_TCPSocket *) pb->Owner();
TCPiopb * tcp = pb->TCP();
register int readin;
if (!tcp->ioResult) {
readin = tcp->csParam.receive.rcvBuffLen;
sock->fBytesReceived = readin;
}
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(tcp->ioResult != inProgress);
}
/*************************************************************************** *
* FUNCTION:
*
* tcp_send_done
*
* DESCRIPTION:
*
* TCP send-done call back
*
* PARAMETERS:
*
* CSK_AnnotatedPB * -- ???
*
*/
void tcp_send_done(CSK_AnnotatedPB *pb)
{
CSK_TCPSocket * sock = (CSK_TCPSocket *) pb->Owner();
TCPiopb * tcp = pb->TCP();
switch (tcp->ioResult) {
case noErr:
((wdsEntry *)(tcp->csParam.send.wdsPtr))->length = 0; /* mark it free */
break;
case ipNoFragMemErr:
case connectionClosing:
case connectionTerminated:
case connectionDoesntExist:
sock->fSocketState = SKk_STATE_UNCONNECTED;
sock->fLastAsyncErr = ENOTCONN;
break;
case ipDontFragErr:
case invalidStreamPtr:
case invalidLength:
case invalidWDS:
default:
sock->fSocketState = SKk_STATE_UNCONNECTED;
sock->fLastAsyncErr = tcp->ioResult;
break;
}
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(tcp->ioResult != inProgress);
}
} /* extern "C" */
/**************************************************************************/
/**************************************************************************/
/* CSK_TCPSocket members */
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::CSK_TCPSocket
*
* DESCRIPTION:
*
* Constructor for this class
*
*/
CSK_TCPSocket::CSK_TCPSocket()
: CSK_INETSocket()
{
TCPiopb pb;
fSocketState = SKk_STATE_UNCONNECTED;
fSelf = new(CSK_TCPSocket *);
*fSelf = this;
pb.ioCRefNum = SKg_INETSockets.Driver();
pb.csCode = TCPCreate;
pb.csParam.create.rcvBuff = (char *) NewPtr(SKk_StreamBufferSize);
pb.csParam.create.rcvBuffLen = SKk_StreamBufferSize;
pb.csParam.create.notifyProc = tcp_notify;
pb.csParam.create.userDataPtr = Ptr(fSelf);
switch(PBControlSync(ParmBlkPtr(&pb)))
{
case noErr: break;
case invalidLength: SK_Error(ENOBUFS); return;
case invalidBufPtr: SK_Error(ENOBUFS); return;
case insufficientResources: SK_Error(EMFILE); return;
default: SK_Error(ENETDOWN); return;
}
fPeerSocketAddress.sin_family = AF_INET;
fPeerSocketAddress.sin_addr.s_addr = 0;
fPeerSocketAddress.sin_port = 0;
bzero(&fPeerSocketAddress.sin_zero[0], 8);
fLastAsyncErr = 0;
fStream = pb.tcpStream;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::CSK_TCPSocket
*
* DESCRIPTION:
*
* Constructor for this class
*
* PARAMETERS:
*
* StreamPtr -- stream to use with this TCP socket
*
*/
CSK_TCPSocket::CSK_TCPSocket(StreamPtr stream)
: CSK_INETSocket(stream)
{
AppleEvent theEvent, myReply;
AEDesc theAddress;
long theType = 'inet';
ProcPtr theProc = (ProcPtr) tcp_notify;
ProcessSerialNumber PSN;
fSelf = new(CSK_TCPSocket *);
*fSelf = this;
fLastAsyncErr = 0;
GetCurrentProcess(&PSN);
AECreateDesc(typeApplSignature, (Ptr) &theType, sizeof(theType), &theAddress);
AECreateAppleEvent('INET', 'TNFY', &theAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
AEPutParamPtr(&theEvent, 'STRM', typeLongInteger, (Ptr) &fStream, sizeof(fStream));
AEPutParamPtr(&theEvent, 'ASR ', typeLongInteger, (Ptr) &theProc, sizeof(ProcPtr));
AEPutParamPtr(&theEvent, 'USRP', typeLongInteger, (Ptr) &fSelf, sizeof(long));
AEPutParamPtr(&theEvent, keyProcessSerialNumber, typeProcessSerialNumber, (Ptr) &PSN, sizeof(ProcessSerialNumber));
AESend(&theEvent, &myReply, kAEWaitReply, kAEHighPriority, 120, nil, nil);
AEDisposeDesc(&myReply);
AEDisposeDesc(&theEvent);
AEDisposeDesc(&theAddress);
TCPiopb * pb;
pb = GetPB();
pb->csCode = TCPStatus;
PBControlSync(ParmBlkPtr(pb));
fMySocketAddress.sin_addr.s_addr = pb->csParam.status.localHost;
fMySocketAddress.sin_port = pb->csParam.status.localPort;
fPeerSocketAddress.sin_addr.s_addr = pb->csParam.status.remoteHost;
fPeerSocketAddress.sin_port = pb->csParam.status.remotePort;
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::CSK_TCPSocket
*
* DESCRIPTION:
*
* Constructor for this class
*
* PARAMETERS:
*
* CSK_TCPSocket * -- Existing TCP socket to used as template for initialization
*
*/
CSK_TCPSocket::CSK_TCPSocket(CSK_TCPSocket * sock)
{
fStream = sock->fStream;
fStatus = sock->fStatus;
fNonBlocking= sock->fNonBlocking;
fReceiveBuf = sock->fReceiveBuf;
fBytesReceived = sock->fBytesReceived;
fMySocketAddress = sock->fMySocketAddress;
fPeerSocketAddress = sock->fPeerSocketAddress;
fSocketState = sock->fSocketState;
fLastAsyncErr = 0;
// The reason for this strange code is that fStream->userData points to
// sock.fSelf and cannot be changed while the stream is alive
fSelf = sock->fSelf;
*fSelf = this;
sock->fSelf = new(CSK_TCPSocket *);
*sock->fSelf = sock;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::~CSK_TCPSocket
*
* DESCRIPTION:
*
* Destructor for this class
*
*/
CSK_TCPSocket::~CSK_TCPSocket()
{
TCPiopb * pb;
do {
pb = GetPB();
pb->csCode = TCPStatus;
PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
SAFESPIN(false, SKk_MISC, 0);
} while (!errno && pb->csParam.status.amtUnackedData > 0);
pb = GetPB();
pb->ioCompletion = nil;
pb->csCode = TCPClose;
pb->csParam.close.validityFlags = timeoutValue | timeoutAction;
pb->csParam.close.ulpTimeoutValue = 60 /* seconds */;
pb->csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
switch (PBControlAsync(ParmBlkPtr(pb)))
{
case noErr:
case connectionClosing:
break;
case connectionDoesntExist:
case connectionTerminated:
break;
case invalidStreamPtr:
default:
return;
}
{
rdsEntry rdsarray[SKk_TCPMaxWds+1];
int passcount;
const int maxpass =4;
pb = GetPB();
for (passcount=0; passcount<maxpass; passcount++) {
pb->csCode = TCPNoCopyRcv;
pb->csParam.receive.commandTimeoutValue = 1; /* seconds, 0 = blocking */
pb->csParam.receive.rdsPtr = (Ptr)rdsarray;
pb->csParam.receive.rdsLength = SKk_TCPMaxWds;
if (PBControlSync(ParmBlkPtr(pb)))
break;
pb->csCode = TCPRcvBfrReturn;
pb->csParam.receive.rdsPtr = (Ptr)rdsarray;
PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
SAFESPIN(false, SKk_MISC, 0);
if (errno)
break;
}
if (passcount == maxpass) { /* remote side isn't being nice */
/* then try again */
PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
for (passcount=0; passcount<maxpass; passcount++) {
pb->csCode = TCPNoCopyRcv;
pb->csParam.receive.commandTimeoutValue = 1; /* seconds, 0 = blocking */
pb->csParam.receive.rdsPtr = (Ptr)rdsarray;
pb->csParam.receive.rdsLength = SKk_TCPMaxWds;
if (PBControlSync(ParmBlkPtr(pb)))
break;
pb->csCode = TCPRcvBfrReturn;
pb->csParam.receive.rdsPtr = (Ptr)rdsarray;
PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
SAFESPIN(false, SKk_MISC, 0);
if (errno)
break;
}
}
}
/* destroy the stream */
pb->csCode = TCPRelease;
if (PBControlSync(ParmBlkPtr(pb)))
return;
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
delete fSelf;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::GetPB
*
* DESCRIPTION:
*
* Get a TCP io parameter block for use with this TCP socket. The
* parameter block is obtained from the SKg_INETSockets domain object
* and is set up for use specifically with this socket.
*
* RETURNS:
*
* TCPiopb * -- The TCP io parameter block
*
*/
TCPiopb * CSK_TCPSocket::GetPB()
{
CSK_AnnotatedPB * pb = SKg_INETSockets.GetPB();
pb->TCP()->ioCRefNum = SKg_INETSockets.Driver();
pb->TCP()->tcpStream = fStream;
pb->SetOwner(this);
return pb->TCP();
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::Available
*
* DESCRIPTION:
*
* Return number of bytes of unread data available on this socket
*
* RETURNS:
*
* u_long -- The number of unread bytes available
*
*/
u_long CSK_TCPSocket::Available()
{
TCPiopb * pb;
pb = GetPB();
pb->csCode = TCPStatus;
PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
return pb->csParam.status.amtUnreadData;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::connect
*
* DESCRIPTION:
*
* Initiate a connection on a MacTCP socket. This call attempts to make a
* connection to another socket. The other socket is specified by an internet
* address and port.
*
* PARAMETERS:
*
* void * -- The internet address and port
* int -- The length of the given internet address information
*
* RETURNS:
*
* If the connection or binding succeeds, then 0 is returned.
* Otherwise a -1 is returned, and a more specific error code is stored in
* errno:
*
* EAFNOSUPPORT The address family in addr is not AF_INET.
*
* EHOSTUNREACH The TCP connection came up half-way and then failed.
*
* NOTES:
*
* TCP sockets may successfully connect() only once;
*
*/
int CSK_TCPSocket::connect(void * address, int addrlen)
{
OSErr err;
struct sockaddr_in * addr = (struct sockaddr_in *) address;
TCPiopb * pb;
ASSERT(addrlen == sizeof(struct sockaddr_in));
if (addr->sin_family != AF_INET)
return SK_Error(EAFNOSUPPORT);
/* Make sure this socket can connect */
if (fSocketState == SKk_STATE_CONNECTING)
return SK_Error(EALREADY);
if (fSocketState != SKk_STATE_UNCONNECTED)
return SK_Error(EISCONN);
fSocketState = SKk_STATE_CONNECTING;
pb = GetPB();
pb->ioCompletion = TCPIOCompletionProc(tcp_connect_done);
pb->csCode = TCPActiveOpen;
pb->csParam.open.validityFlags = timeoutValue | timeoutAction;
pb->csParam.open.ulpTimeoutValue = 60 /* seconds */;
pb->csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */;
pb->csParam.open.commandTimeoutValue = 0;
pb->csParam.open.remoteHost = addr->sin_addr.s_addr;
pb->csParam.open.remotePort = addr->sin_port;
pb->csParam.open.localHost = 0;
pb->csParam.open.localPort = fMySocketAddress.sin_port;
pb->csParam.open.dontFrag = 0;
pb->csParam.open.timeToLive = 0;
pb->csParam.open.security = 0;
pb->csParam.open.optionCnt = 0;
err = PBControlAsync(ParmBlkPtr(pb));
if (err != 0)
{
fSocketState = SKk_STATE_UNCONNECTED;
return SK_TCPError(err);
}
if (fNonBlocking)
return SK_Error(EINPROGRESS);
/* sync connect - spin till TCPActiveOpen completes */
SAFESPIN(pb->ioResult==inProgress, SKk_MISC, 0);
if (errno || pb->ioResult) {
fSocketState = SKk_STATE_UNCONNECTED;
if (errno)
return -1;
else
return SK_TCPError(pb->ioResult);
} else
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::listen
*
* DESCRIPTION:
*
* Listen for a connection on this MacTCP socket. When a connection is
* requested, the TCPIOCompletionProc is called to handle establishing
* the connection (which is done by creating a new socket for that connection
* and then returning this socket to a listening state).
*
* PARAMETERS:
*
* int -- unused
*
* RETURNS:
*
* int -- Error value (0 = no error). When error occurs, the global
* errno contains a more detailed error value.
*
*/
int CSK_TCPSocket::listen(int)
{
OSErr err;
TCPiopb * pb;
if (fSocketState != SKk_STATE_UNCONNECTED)
return SK_Error(EISCONN);
fSocketState = SKk_STATE_LISTENING;
pb = GetPB();
pb->ioCRefNum = SKg_INETSockets.Driver();
pb->ioCompletion = TCPIOCompletionProc(tcp_listen_done);
pb->csCode = TCPPassiveOpen;
pb->csParam.open.validityFlags = timeoutValue | timeoutAction;
pb->csParam.open.ulpTimeoutValue = 255 /* seconds */;
pb->csParam.open.ulpTimeoutAction = 0 /* 1:abort 0:report */;
pb->csParam.open.commandTimeoutValue = 0 /* infinity */;
pb->csParam.open.remoteHost = 0;
pb->csParam.open.remotePort = 0;
pb->csParam.open.localHost = 0;
pb->csParam.open.localPort = fMySocketAddress.sin_port;
pb->csParam.open.dontFrag = 0;
pb->csParam.open.timeToLive = 0;
pb->csParam.open.security = 0;
pb->csParam.open.optionCnt = 0;
err = PBControlAsync(ParmBlkPtr(pb));
if (err != 0) {
fSocketState = SKk_STATE_UNCONNECTED;
return SK_TCPError(err);
}
SAFESPIN(!pb->csParam.open.localPort, SKk_MISC, 0);
if (errno) {
fSocketState = SKk_STATE_UNCONNECTED;
return -1;
}
fMySocketAddress.sin_addr.s_addr = pb->csParam.open.localHost;
fMySocketAddress.sin_port = pb->csParam.open.localPort;
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::accept
*
* DESCRIPTION:
*
* Accept a connection request from a given host.
*
* PARAMETERS:
*
* void * -- The internet number of the requesting host
* int -- The length of the above address structure
*
* RETURNS:
*
* CSK_Socket * -- A newly created socket that will handle the newly
* established connection (or nil if failed).
*
*/
CSK_Socket * CSK_TCPSocket::accept(void *from, int *fromlen)
{
CSK_TCPSocket * sock;
TCPiopb * pb;
int err;
if (fSocketState == SKk_STATE_UNCONNECTED)
if (fLastAsyncErr) {
SK_TCPError(fLastAsyncErr);
fLastAsyncErr = 0;
return nil;
} else
return (CSK_Socket *) SK_Error_nil(ENOTCONN);
if (fSocketState != SKk_STATE_LISTENING && fSocketState != SKk_STATE_LIS_CON)
return (CSK_Socket *) SK_Error_nil(ENOTCONN);
if (fSocketState == SKk_STATE_LISTENING) {
if (fNonBlocking)
return (CSK_Socket *) SK_Error_nil(EWOULDBLOCK);
/* Spin till sock_tcp_listen_done runs. */
SPINP(fSocketState == SKk_STATE_LISTENING, SKk_MISC, 0);
/* got notification - was it success? */
if (fSocketState != SKk_STATE_LIS_CON) {
(void) SK_TCPError(fLastAsyncErr);
fLastAsyncErr = 0;
return nil;
}
}
/*
* Have connection. Duplicate this socket. The client gets the connection
* on the new socket and I create a new stream on the old socket and put it
* in listen state.
*/
fSocketState = SKk_STATE_CONNECTED;
sock = new CSK_TCPSocket(this);
if (!sock)
{
/* Abort the incoming connection. */
pb = GetPB();
pb->csCode = TCPAbort;
PBControlSync(ParmBlkPtr(pb));
fSocketState = SKk_STATE_UNCONNECTED;
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
/* try and put the socket back in listen mode */
if (listen(5) < 0)
{
fSocketState = SKk_STATE_UNCONNECTED;
return nil; /* errno already set */
}
return (CSK_Socket *) SK_Error_nil(ENOMEM);
}
/* Create a new MacTCP stream on the old socket and put it into */
/* listen state to accept more connections. */
fSocketState = SKk_STATE_UNCONNECTED;
pb = GetPB();
pb->csCode = TCPCreate;
pb->csParam.create.rcvBuff = (char *)NewPtr(SKk_StreamBufferSize);
pb->csParam.create.rcvBuffLen = SKk_StreamBufferSize;
pb->csParam.create.notifyProc = tcp_notify;
pb->csParam.create.userDataPtr = Ptr(fSelf);
err = PBControlSync(ParmBlkPtr(pb));
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
switch(err)
{
case noErr: break;
case invalidLength: return (CSK_Socket *) SK_Error_nil(ENOBUFS);
case invalidBufPtr: return (CSK_Socket *) SK_Error_nil(ENOBUFS);
case insufficientResources: return (CSK_Socket *) SK_Error_nil(EMFILE);
default: return (CSK_Socket *) SK_Error_nil(ENETDOWN);
}
fPeerSocketAddress.sin_family = AF_INET;
fPeerSocketAddress.sin_addr.s_addr = 0;
fPeerSocketAddress.sin_port = 0;
bzero(&fPeerSocketAddress.sin_zero[0], 8);
fLastAsyncErr = 0;
fStream = pb->tcpStream;
if (listen(5) < 0) {
/* nothing to listen on */
fSocketState = SKk_STATE_UNCONNECTED;
/* kill the incoming connection */
pb = sock->GetPB();
pb->csCode = TCPRelease;
if (!PBControlSync(ParmBlkPtr(pb)))
DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
return nil; /* errno set */
}
/* return address of partner */
memcpy(from, &fPeerSocketAddress, *fromlen = MIN(*fromlen, sizeof(struct sockaddr_in)));
return sock;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::recvfrom
*
* DESCRIPTION:
*
* recvfrom() attempts to receive a message (ie a datagram) on this socket
*
* PARAMETERS:
*
* void * -- Pointer to buffer for returned bytes
* int -- Maximum number of bytes to read
* int -- unused
* void * -- Pointer to memory for return value (address of sender)
* int * -- Pointer to memory for length of return value
*
* RETURNS:
*
* int -- Error return (0 = no error), -1 = error in which case
* a more specific error code is stored in the global errno:
*
* ESHUTDOWN The socket has been shutdown for receive operations.
*
* The bytes received are read into the given buffer.
*
* The internet address of the caller and the length of that address
* are placed into the last two parameters (void * and int).
*
* NOTES:
*
* Typically, read() is used with a TCP stream and recv() with
* UDP where the idea of a message makes more sense. But in fact,
* read() and recv() are equivalent.
*
* Regardless of non-blocking status, if less data is available
* than has been requested, only that much data is returned.
*
* If the socket is marked for non-blocking I/O, and the socket
* is empty, the operation will fail with the error EWOULDBLOCK.
* Otherwise, the operation will block until data is available
* or an error occurs.
*
* A return value of zero indicates that the stream has been
* closed and all data has already been read. ie. end-of-file.
*
*/
int CSK_TCPSocket::recvfrom(void * buffer, int buflen, int, void * from, int *fromlen)
{
TCPiopb * pb;
u_long dataavail;
if (from)
getpeername(from, fromlen);
if (fStatus & SKk_STATUS_NOREAD)
return SK_Error(ESHUTDOWN);
/* socket hasn't finished connecting yet */
if (fSocketState == SKk_STATE_CONNECTING)
{
if (fNonBlocking)
return SK_Error(EWOULDBLOCK);
/* async connect and sync recv? */
SPIN(fSocketState == SKk_STATE_CONNECTING,SKk_MISC,0);
}
/* socket is not connected */
if (!(fSocketState == SKk_STATE_CONNECTED))
{
/* see if the connect died (pretty poor test) */
if (fSocketState == SKk_STATE_UNCONNECTED && fLastAsyncErr != 0 && fLastAsyncErr != 1)
{
(void) SK_TCPError(fLastAsyncErr);
fLastAsyncErr = 0;
return -1;
}
/* I guess he just forgot */
return SK_Error(ENOTCONN);
}
dataavail = Available();
if (fNonBlocking && !dataavail)
return SK_Error(EWOULDBLOCK);
fReceiveBuf = (char *) buffer;
fBytesReceived = 0;
fLastAsyncErr = inProgress;
pb = GetPB();
pb->ioCompletion = TCPIOCompletionProc(tcp_recv_done);
pb->csCode = TCPRcv;
pb->csParam.receive.commandTimeoutValue = 0; /* seconds, 0 = blocking */
pb->csParam.receive.rcvBuff = fReceiveBuf;
pb->csParam.receive.rcvBuffLen = MIN(buflen,SKk_TCPMaxMessageSize);
PBControlAsync(ParmBlkPtr(pb));
/* This is potentially dangerous, as there doesn't seem to be a way to
stop the receive call on an user abort.
*/
SPIN(pb->ioResult==inProgress, SKk_STREAM_READ, buflen);
if (pb->ioResult == commandTimeout)
pb->ioResult = noErr;
switch(pb->ioResult)
{
case noErr:
fLastAsyncErr = noErr;
return fBytesReceived;
case connectionClosing:
case connectionTerminated:
return fBytesReceived;
case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
case connectionDoesntExist:
case invalidStreamPtr:
case invalidLength:
case invalidBufPtr:
default:
return SK_TCPError(pb->ioResult);
}
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::sendto
*
* DESCRIPTION:
*
* sendto() is used to transmit a message from this socket to another socket.
*
* PARAMETERS:
*
* void * -- Pointer to buffer containing bytes to send
* int -- The number of bytes to send
* int -- Flags (???)
* void * -- Pointer to internet address to send to
* int -- Length of above internet address structure
*
* RETURNS:
*
* This call returns the number of bytes sent, or -1 if an error occurred,
* in which case the global errno contains a more detailed error value:
*
* EINVAL The sum of the iov_len values in the iov array was
* greater than 65535 (TCP) or 65507 (UDP) or there
* were too many entries in the array (16 for TCP or
* 6 for UDP).
*
* ESHUTDOWN The socket has been shutdown for send operations.
*
* EMSGSIZE The message is too big to send in one datagram. (UDP)
*
* ENOBUFS The transmit queue is full. (UDP)
*
* The bytes received are read into the given buffer.
*
* NOTES:
*
* Typically, write() is used with a TCP stream and send() with
* UDP where the idea of a message makes more sense. But in fact,
* write() and send() are equivalent.
*
* Write() and send() operations are not considered complete
* until all data has been sent and acknowledged.
*
* If a socket is marked for non-blocking I/O, the operation
* will return an 'error' of EINPROGRESS.
*
* If the socket is not marked for non-blocking I/O, the write will
* block until space becomes available.
*
* write() and send() may be used only when the socket is in a connected
* state, sendto() may be used at any time.
*
*/
int CSK_TCPSocket::sendto(void * buffer, int count, int flags, void * to, int)
{
int bytes,towrite;
SKt_MiniWds * thiswds;
short wdsnum;
TCPiopb * pb;
SKt_MiniWds wdsarray[SKk_TCPMaxWds];
if (fStatus & SKk_STATUS_NOWRITE)
return SK_Error(ESHUTDOWN);
if (to != NULL) /* sendto */
return SK_Error(EOPNOTSUPP);
if (fSocketState != SKk_STATE_CONNECTED && fSocketState != SKk_STATE_CONNECTING)
return SK_Error(ENOTCONN);
/* socket hasn't finished connecting yet */
if (fSocketState == SKk_STATE_CONNECTING) {
if (fNonBlocking)
return SK_Error(EALREADY);
/* async connect and sync send? */
SPIN(fSocketState == SKk_STATE_CONNECTING, SKk_MISC, 0);
}
/* socket is not connected */
if (!(fSocketState == SKk_STATE_CONNECTED)) {
/* see if a previous operation failed */
if (fSocketState == SKk_STATE_UNCONNECTED && fLastAsyncErr != 0) {
(void) SK_TCPError(fLastAsyncErr);
fLastAsyncErr = 0;
return -1;
}
/* I guess he just forgot */
return SK_Error(ENOTCONN);
}
pb = GetPB();
pb->csCode = TCPStatus;
if (PBControlSync(ParmBlkPtr(pb)))
bytes = 0;
else {
bytes = pb->csParam.status.sendWindow - pb->csParam.status.amtUnackedData;
if (bytes < 0)
bytes = 0;
}
// Check for nasty error that once plagued us (will be removed eventually)
ASSERT(pb->ioResult != inProgress);
if (fNonBlocking)
if (!bytes)
return SK_Error(EWOULDBLOCK);
else if (bytes < count)
count = bytes;
bytes = count; /* save count before we nuke it */
memset(wdsarray, 0, SKk_TCPMaxWds*sizeof(SKt_MiniWds)); /* clear up terminus and mark empty */
thiswds = wdsarray;
wdsnum = 0;
while (count > 0) {
/* make sure the thing that just finished worked ok */
if (fLastAsyncErr) {
(void) SK_Error(fLastAsyncErr);
fLastAsyncErr = 0;
return -1;
}
towrite=MIN(count,SKk_TCPMaxMessageSize);
/* find a clean wds */
while (thiswds->length != 0) {
wdsnum = (short)((wdsnum+1)%SKk_TCPMaxWds); /* generates compiler warning w/o short - why? */
if (wdsnum)
thiswds++;
else
thiswds = wdsarray;
SPIN(false, SKk_STREAM_WRITE, count); /* spin once */
}
/* find a clean pb */
thiswds->length = (short)towrite;
thiswds->ptr = (char *) buffer;
pb = GetPB();
pb->ioCompletion = TCPIOCompletionProc(tcp_send_done);
pb->csCode = TCPSend;
pb->csParam.send.validityFlags = timeoutValue | timeoutAction;
pb->csParam.send.ulpTimeoutValue = 60 /* seconds */;
pb->csParam.send.ulpTimeoutAction = 1 /* 0:abort 1:report */;
pb->csParam.send.pushFlag = count <= SKk_TCPMaxMessageSize;
pb->csParam.send.urgentFlag = flags & MSG_OOB;
pb->csParam.send.wdsPtr = (Ptr)thiswds;
pb->csParam.send.sendFree = 0;
pb->csParam.send.sendLength = 0;
PBControlAsync(ParmBlkPtr(pb));
SPIN(false, SKk_STREAM_WRITE, count);
count -= towrite;
buffer = (char *) buffer + towrite;
}
SPIN(pb->ioResult == inProgress, SKk_STREAM_WRITE, 0);
if (!pb->ioResult)
return(bytes);
else
return SK_TCPError(pb->ioResult);
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_TCPSocket::select
*
* DESCRIPTION:
*
* Check this socket for readiness to read or write, or for whether
* it has an exceptional condition pending. Each of the three
* flags passed as parameters are set accordingly; only those which
* were initially set to TRUE are checked.
*
* PARAMETERS:
*
* Boolean * -- Can read (???)
* Boolean * -- Can write (???)
* Boolean * -- Exception (???)
*
* RETURNS:
*
* int -- The total number of return flags set to TRUE.
*
*/
int CSK_TCPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean * )
{
int goodies = 0;
if (canRead)
switch (fSocketState) {
case SKk_STATE_LIS_CON:
*canRead = true;
++goodies;
break;
case SKk_STATE_CONNECTED:
if (Available()) {
*canRead = true;
++goodies;
}
break;
case SKk_STATE_UNCONNECTED:
case SKk_STATE_CLOSING:
*canRead = true; /* CAN THIS REALLY BE CORRECT ??!?!? S.D. 9/1/93 */
++goodies; /* Should either be Available() or FALSE ?? */
break;
}
if (canWrite)
switch (fSocketState) {
case SKk_STATE_CONNECTING:
break;
default:
*canWrite = true;
++goodies;
}
return goodies;
}
/* end of SK_TCP.cc */